package algorithm;

import entity.DeviceType;
import entity.FourTuple;
import entity.TwoTuple;
import problem.MLSIAP;
import problem.Problem;
import solution.ParticleForMLSIAP;
import util.GeneralUtil;

/**
 * 根据BiNPSO(1997)文献修改
 * 
 * @author Dehuai Liu
 *
 */
public class PSOAlgForMLSIAP
		extends PSOAlg<Integer, Double, ParticleForMLSIAP, FourTuple<Double, Double, Integer, Double>> {

	double wMax;
	double wMin;

	MLSIAP Mproblem;
	int instanceId;
	int N1, N2, N;
	int M1, M2, M;
	int groupUpper, groupLower;
	int S;
	int L;

	public PSOAlgForMLSIAP(Problem<Integer, FourTuple<Double, Double, Integer, Double>, Double> problem, boolean isMax,
			String algName, int popSize, int NFE, double wMax, double wMin, double c1, double c2, Integer Vmax) {
		super(problem, isMax, algName, popSize, NFE, wMax, c1, c2, Vmax);
		this.wMax = wMax;
		this.wMin = wMin;
		Mproblem = (MLSIAP) problem;
		getParameters();
	}

	private void getParameters() {
		instanceId = Mproblem.getInstanceId();
		N1 = Mproblem.getN1();
		N2 = Mproblem.getN2();
		N = Mproblem.getN();
		M1 = Mproblem.getM1();
		M2 = Mproblem.getM2();
		M = Mproblem.getM();
		groupUpper = Mproblem.getUpper();
		groupLower = Mproblem.getLower();
		S = Mproblem.getS();
		L = groupUpper - groupLower;
	}

	@Override
	public void initialize() {
		for (int i = 0; i < popSize; i++) {
			Integer[] content = new Integer[N + M];
			// 产生乘客分组的解
			for (int j = 0; j < N; j++) {
				content[j] = random.nextInt(groupUpper - groupLower + 1) + groupLower;
			}
			// 产生设备人员分配的解
			assignInspectors(content);
			// 初始化速度
			double[] vs = new double[N];
			for (int j = 0; j < N; j++) {
				vs[j] = random.nextDouble() * 2 * Vmax - Vmax;
			}

			ParticleForMLSIAP particle = new ParticleForMLSIAP(content, groupUpper, groupLower, i,
					Mproblem.getDimension(), vs);
			population.add(particle);
		}
	}

	/**
	 * 根据每个设备th=Th情况下，检查人员检查乘客的总时间，降序排序，从前往后分配检查人员
	 * 
	 * @param content
	 */
	private void assignInspectors(Integer[] content) {
		DeviceType[] devices = Mproblem.calTotolTimeOfEveryDevice(content);
		// 防止空指针异常
		for (int i = N; i < N + M; i++) {
			content[i] = 0;
		}
		int temp = S;
		for (int i = 0; i < devices.length; i++) {
			// 没有可以额外分配的检查人员
			if (temp == 0) {
				break;
			} else if (temp >= devices[i].getNDh()) {
				content[N + devices[i].getH() - 1] = devices[i].getNDh();
				// 把分配的检查人员减掉
				temp -= devices[i].getNDh();
			} else {
				content[N + devices[i].getH() - 1] = temp;
				temp = 0;
			}
		}
	}

	/**
	 * 评估一个粒子
	 * 
	 * @param particle
	 */
	protected void evaluateOnePartilc(ParticleForMLSIAP particle) {
		// 计算适应度和约束值
		FourTuple<Double, Double, Integer, Double> result = Mproblem.calculate(particle);
		particle.setValue(result.getFirst());
		particle.setValueOfPFC(result.getSecond());
		particle.setTotalS(result.getThrid());
		particle.setMaxTime(result.getFourth());
		// 增加函数评估次数
		current_NFE++;
		// 统计
		if (current_NFE % 1000 == 0) {
			TwoTuple<Integer, ParticleForMLSIAP> record = new TwoTuple<Integer, ParticleForMLSIAP>(current_NFE, best);
			records.add(record);
		}
	}

	@Override
	protected void evaluate() {
		best = population.get(0);
		// 更新每个粒子的函数值，并且更新pbest和gbest
		for (ParticleForMLSIAP particle : population) {
			// 更新每个粒子的函数值
			evaluateOnePartilc(particle);
			if (particle.getValue() < best.getValue()) {
				best = particle;
			}

			try {
				// 设置历史最优
				particle.setpBest((ParticleForMLSIAP) particle.clone());
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}

		try {
			// 粒子的content会不断发生改变，需要将best单独保存在一个对象里
			best = (ParticleForMLSIAP) best.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 线性减小w
	 */
	@Override
	public void updateParameters() {
		w = wMax - (wMax - wMin) * (current_NFE / NFE);
	}

	@Override
	public void evolve() {
		for (int i = 0; i < popSize; i++) {
			ParticleForMLSIAP particle = population.get(i);
			// 移动粒子
			moveParticle(particle);
			// 评估
			evaluateOnePartilc(particle);
			// 更新pbest
			if (particle.getValue() < particle.getpBest().getValue()) {
				try {
					particle.setpBest((ParticleForMLSIAP) particle.clone());
					// 更新gbest
					if (particle.getValue() < best.getValue()) {
						best = (ParticleForMLSIAP) particle.clone();
					}
				} catch (CloneNotSupportedException e) {
					e.printStackTrace();
				}

			}

		}
	}

	public void moveParticle(ParticleForMLSIAP particle) {
		double[] vs = particle.getVs();
		Integer[] content = particle.getContent();
		Integer[] pbestContent = particle.getpBest().getContent();
		Integer[] gbestContent = best.getContent();
		for (int d = 0; d < N; d++) {
			// 更新速度
			vs[d] = vs[d] + c1 * random.nextDouble() * (pbestContent[d] - content[d])
					+ c2 * random.nextDouble() * (gbestContent[d] - content[d]);
			// 速度边界检查
			if (vs[d] > Vmax) {
				vs[d] = Vmax;
			} else if (vs[d] < -Vmax) {
				vs[d] = -Vmax;
			}

			if (random.nextDouble() < GeneralUtil.sigmoid(vs[d])) {
				content[d] = random.nextInt(groupUpper - groupLower + 1) + groupLower;
			}

			// 位置边界检查
			checkBound(groupUpper, groupLower, content, d);
			// 更新检查人员分配
			assignInspectors(content);
		}
	}

	/**
	 * 
	 * @param upper
	 * @param lower
	 * @param content
	 * @param d
	 *            维度
	 * @return
	 */
	protected boolean checkBound(int upper, int lower, Integer[] content, int d) {
		if (content[d] >= lower && content[d] <= upper) {
			return true;
		}
		content[d] = random.nextInt((upper - lower + 1)) + lower;
		return false;
	}

}
